#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Android资源处理器
同步处理代码和布局文件中的资源ID引用
"""

import os
import re
import xml.etree.ElementTree as ET
from typing import Dict, List, Set, Tuple
from pathlib import Path
from ..utils_module.language_utils import collect_extensions, language_to_extensions

class AndroidResourceHandler:
    """Android资源处理器"""
    
    def __init__(self, config, logger):
        self.config = config
        self.logger = logger
        
    def sync_resource_ids(self, project_path: str, symbol_map: Dict[str, str]) -> Dict[str, str]:
        """
        同步资源ID的混淆
        确保代码中的findViewById和布局文件中的android:id保持一致
        返回资源ID映射表
        """
        # 1. 创建资源ID映射表
        resource_id_map = self._create_resource_id_map(project_path, symbol_map)
        
        # 2. 更新布局文件中的ID
        layout_files = self._find_layout_files(project_path)
        for layout_file in layout_files:
            self._update_layout_ids(layout_file, resource_id_map)
        
        # 3. 更新Kotlin代码中的资源ID引用
        if resource_id_map:
            self._update_kotlin_resource_ids(project_path, resource_id_map)
        
        # 3.5 处理其他资源类型
        additional_types = ['string', 'drawable', 'color']
        additional_maps = {}
        for resource_type in additional_types:
            mapping = self._create_generic_resource_map(project_path, symbol_map, resource_type)
            if not mapping:
                continue
            additional_maps[resource_type] = mapping
            self._update_xml_resource_references(project_path, resource_type, mapping)
            self._update_code_resource_references(project_path, resource_type, mapping)

        # 4. 记录处理结果
        if resource_id_map:
            self.logger.log_operation("资源ID同步", f"处理了 {len(resource_id_map)} 个资源ID")
        for resource_type, mapping in additional_maps.items():
            self.logger.log_operation(
                "资源同步",
                f"{resource_type} 资源同步 {len(mapping)} 项"
            )

        return resource_id_map
    
    def _create_resource_id_map(self, project_path: str, symbol_map: Dict[str, str]) -> Dict[str, str]:
        """
        创建资源ID映射表
        扫描布局文件中的所有ID，并根据符号映射创建对应关系
        """
        resource_id_map = {}
        code_extensions = collect_extensions(['kotlin', 'java'])
        layout_files = self._find_layout_files(project_path)
        
        # 1. 扫描所有布局文件，收集现有的资源ID
        existing_ids = set()
        for layout_file in layout_files:
            try:
                with open(layout_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # 查找所有的 android:id="@+id/xxx" 和 android:id="@id/xxx"
                id_pattern = r'android:id="@\+?id/([^"]+)"'
                ids = re.findall(id_pattern, content)
                existing_ids.update(ids)
                
            except Exception as e:
                self.logger.log_error(f"扫描布局文件失败: {layout_file}, 错误: {str(e)}")
        
        # 2. 扫描代码文件，收集R.id.xxx引用
        code_ids = set()
        for root, dirs, files in os.walk(project_path):
            for file in files:
                if any(file.endswith(ext) for ext in code_extensions):
                    try:
                        with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
                            content = f.read()
                        # 查找R.id.xxx引用
                        id_pattern = r'R\.id\.(\w+)'
                        ids = re.findall(id_pattern, content)
                        code_ids.update(ids)
                    except Exception:
                        pass
        
        # 3. 为每个资源ID创建映射
        all_ids = existing_ids | code_ids
        for resource_id in all_ids:
            # 尝试从符号映射中查找对应的混淆名
            # 资源ID通常是snake_case，需要转换为camelCase来查找
            camel_case_id = self._snake_to_camel(resource_id)
            
            # 查找各种可能的变量名形式
            possible_names = [
                resource_id,           # 原始形式
                camel_case_id,         # 驼峰形式
                camel_case_id + 'View',     # 加View后缀
                camel_case_id + 'Button',   # 加Button后缀
                camel_case_id + 'TextView', # 加TextView后缀
                camel_case_id + 'EditText', # 加EditText后缀
                camel_case_id + 'ImageView', # 加ImageView后缀
            ]
            
            # 在符号映射中查找
            for name in possible_names:
                if name in symbol_map:
                    # 找到了对应的混淆名
                    obfuscated_name = symbol_map[name]
                    # 转换为snake_case作为新的资源ID
                    new_resource_id = self._camel_to_snake(obfuscated_name)
                    resource_id_map[resource_id] = new_resource_id
                    self.logger.log_operation("资源ID映射", f"{resource_id} -> {new_resource_id}")
                    break
        
        return resource_id_map

    def _create_generic_resource_map(self, project_path: str, symbol_map: Dict[str, str], resource_type: str) -> Dict[str, str]:
        """为 string/drawable 等资源类型创建映射"""
        detected_names: Set[str] = set()
        xml_pattern = re.compile(rf'@{resource_type}/([A-Za-z0-9_]+)')
        code_pattern = re.compile(rf'R\.{resource_type}\.([A-Za-z0-9_]+)')
        code_extensions = collect_extensions(['kotlin', 'java'])

        for root, _, files in os.walk(project_path):
            for file in files:
                if not (file.endswith('.xml') or any(file.endswith(ext) for ext in code_extensions)):
                    continue
                path = Path(root) / file
                try:
                    content = path.read_text(encoding='utf-8')
                except (UnicodeDecodeError, OSError):
                    continue

                detected_names.update(xml_pattern.findall(content))
                detected_names.update(code_pattern.findall(content))

        resource_map: Dict[str, str] = {}
        for name in detected_names:
            camel_case = self._snake_to_camel(name)
            candidates = [name, camel_case]

            if resource_type == 'string':
                candidates.extend([
                    camel_case + 'Text',
                    camel_case + 'Message',
                    camel_case + 'Title'
                ])
            elif resource_type == 'drawable':
                candidates.extend([
                    camel_case + 'Drawable',
                    camel_case + 'Icon',
                    camel_case + 'Image'
                ])
            elif resource_type == 'color':
                candidates.extend([
                    camel_case + 'Color',
                    'color' + camel_case.capitalize()
                ])

            for candidate in candidates:
                if candidate in symbol_map:
                    obfuscated = symbol_map[candidate]
                    new_name = self._camel_to_snake(obfuscated)
                    resource_map[name] = new_name
                    break

        return resource_map
    
    def _snake_to_camel(self, snake_str: str) -> str:
        """下划线转驼峰"""
        components = snake_str.split('_')
        # 第一个单词保持小写，其余单词首字母大写
        return components[0] + ''.join(x.title() for x in components[1:])
    
    def _find_layout_files(self, project_path: str) -> List[str]:
        """查找所有布局文件"""
        layout_files = []
        
        # Android布局文件通常在 res/layout 目录
        for root, dirs, files in os.walk(project_path):
            if 'res/layout' in root or 'res\\layout' in root:
                for file in files:
                    if file.endswith('.xml'):
                        layout_files.append(os.path.join(root, file))
        
        return layout_files
    
    def _update_layout_ids(self, layout_file: str, resource_id_map: Dict[str, str]):
        """更新布局文件中的ID"""
        try:
            # 读取XML文件
            with open(layout_file, 'r', encoding='utf-8') as f:
                content = f.read()
            
            original_content = content
            
            # 查找并替换 android:id="@+id/xxx"
            for original_id, new_id in resource_id_map.items():
                # 替换 @+id/ 引用
                pattern = f'@+id/{original_id}'
                replacement = f'@+id/{new_id}'
                content = content.replace(pattern, replacement)
                
                # 也替换 @id/ 引用（没有+号的）
                pattern = f'@id/{original_id}'
                replacement = f'@id/{new_id}'
                content = content.replace(pattern, replacement)
            
            # 写回文件
            if content != original_content and not self.config.dry_run:
                with open(layout_file, 'w', encoding='utf-8') as f:
                    f.write(content)
                self.logger.log_file_processing(layout_file, "已更新资源ID")
                
        except Exception as e:
            self.logger.log_error(f"更新布局文件失败: {layout_file}, 错误: {str(e)}")

    def _camel_to_snake(self, name: str) -> str:
        """驼峰命名转下划线命名"""
        # 移除常见的视图后缀
        for suffix in ['TextView', 'Button', 'View', 'Layout', 'Edit']:
            if name.endswith(suffix):
                name = name[:-len(suffix)]
                break
        
        # 转换为下划线命名
        s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
        return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()

    def _update_xml_resource_references(self, project_path: str, resource_type: str, resource_map: Dict[str, str]):
        """在所有XML文件中替换资源引用"""
        if not resource_map:
            return

        for root, _, files in os.walk(project_path):
            for file in files:
                if not file.endswith('.xml'):
                    continue
                path = Path(root) / file
                try:
                    content = path.read_text(encoding='utf-8')
                except (UnicodeDecodeError, OSError):
                    continue

                original_content = content
                for original_name, new_name in resource_map.items():
                    prefixes = [f'@{resource_type}/{original_name}']
                    if resource_type == 'id':
                        prefixes.append(f'@+id/{original_name}')

                    for prefix in prefixes:
                        replacement = prefix.replace(original_name, new_name)
                        content = content.replace(prefix, replacement)

                if content != original_content and not self.config.dry_run:
                    path.write_text(content, encoding='utf-8')
                    self.logger.log_file_processing(str(path), f"更新{resource_type}资源引用")
    
    def _update_kotlin_resource_ids(self, project_path: str, resource_id_map: Dict[str, str]):
        """保留旧接口: 更新Kotlin和Java代码中的资源ID引用"""
        self._update_code_resource_references(project_path, 'id', resource_id_map)

    def _update_code_resource_references(self, project_path: str, resource_type: str, resource_map: Dict[str, str]):
        """更新代码中的资源引用"""
        if not resource_map:
            return

        # 查找所有Kotlin和Java文件
        code_files = []
        kotlin_exts = set(language_to_extensions('kotlin'))
        java_exts = set(language_to_extensions('java'))
        code_extensions = kotlin_exts.union(java_exts)
        for root, dirs, files in os.walk(project_path):
            for file in files:
                if any(file.endswith(ext) for ext in code_extensions):
                    code_files.append(os.path.join(root, file))
        
        # 更新每个代码文件中的资源ID引用
        for code_file in code_files:
            try:
                with open(code_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                original_content = content
                
                # 替换R.id.xxx引用
                for original_id, new_id in resource_map.items():
                    # 处理各种可能的驼峰命名形式
                    camel_original = self._snake_to_camel(original_id)

                    # 替换 R.<type>.snake_case 格式
                    pattern = rf'\bR\.{resource_type}\.{re.escape(original_id)}\b'
                    replacement = f'R.{resource_type}.{new_id}'
                    content = re.sub(pattern, replacement, content)

                    # 替换 R.<type>.camelCase 格式（如果代码中使用了驼峰形式）
                    pattern = rf'\bR\.{resource_type}\.{re.escape(camel_original)}\b'
                    camel_new = self._snake_to_camel(new_id)
                    replacement = f'R.{resource_type}.{camel_new}'
                    content = re.sub(pattern, replacement, content)

                    # 对于Java文件，还需要处理常见查找方法
                    if code_file.endswith('.java') and resource_type == 'id':
                        # 处理 findViewById(R.id.xxx) 形式
                        pattern = rf'findViewById\s*\(\s*R\.id\.{re.escape(original_id)}\s*\)'
                        replacement = f'findViewById(R.id.{new_id})'
                        content = re.sub(pattern, replacement, content)

                        # 处理驼峰形式
                        pattern = rf'findViewById\s*\(\s*R\.id\.{re.escape(camel_original)}\s*\)'
                        replacement = f'findViewById(R.id.{camel_new})'
                        content = re.sub(pattern, replacement, content)

                # 如果有变化，写回文件
                if content != original_content and not self.config.dry_run:
                    with open(code_file, 'w', encoding='utf-8') as f:
                        f.write(content)
                    file_type = "Kotlin" if any(code_file.endswith(ext) for ext in kotlin_exts) else "Java"
                    self.logger.log_file_processing(code_file, f"已更新{file_type}资源ID引用")
                    
            except Exception as e:
                self.logger.log_error(f"更新代码文件资源ID失败: {code_file}, 错误: {str(e)}")
    
    def protect_resource_references(self, content: str) -> List[Tuple[int, int]]:
        """
        返回需要保护的资源引用区域
        避免R.id.xxx被混淆
        """
        protected_regions = []
        
        # 保护 R.xxx.yyy 格式的资源引用
        for match in re.finditer(r'R\.\w+\.\w+', content):
            protected_regions.append((match.start(), match.end()))
        
        return protected_regions
